from kivy.logger import Logger

from irc.client import NickMask

from amgineirc.viewModel.channels.sendReceiveViewModel import SendReceiveViewModel
from amgineirc.viewModel.channels.vmHelper.ruleVMHelper import RuleVMHelper, SendRuleVMHelper, ReceiveRuleVMHelper


class DccViewModel(SendReceiveViewModel):
    """
    """
    def __init__(self,
                 view, send_view, receive_view,
                 connection,
                 nick,
                 trusted_user_record,
                 appdata):
        SendReceiveViewModel.__init__(self,
                                      view,
                                      send_view,
                                      receive_view,
                                      connection,
                                      nick,
                                      appdata)
        self._last_source = ''
        self._default_command = ''
        # find the rules
        if trusted_user_record is not None:
            # find the rules
            self._amgine = appdata['rules'].get(trusted_user_record['rule_name'])
            self._rule_name = trusted_user_record['rule_name']
        else:
            self._amgine = None
        self._nick = 'Out to ' + self.target
        # ruleshake
        self._receiveRuleVMHelper = None
        self._sendRuleVMHelper = None

    @property
    def server(self):
        if self._server is None and self._connection is not None:
            self._server = self._connection.peeraddress
        return self._server

    # recieve view methods
    
    def handle_client_event(self, evtarget, evtype, evserver, evsource, evargs):
        """
        handle an event from the client
        store event and start the logger to the view
        """
        # evtarget = None
        # evtype = 'dccmsg'
        # evserver = peeraddress
        # evsource = peeraddress
        # evargs = ['asdf...jlk']
        if evtype == 'exception':
            message = self._format_new_log_entry(evtype, evtarget, self.target, evargs)
        elif evtype == 'dccmsg':
            args = evargs[0].split()
            if args[0] == RuleVMHelper.ruleshake_request and len(args) == 3:
                # peer wants to send a rule
                # 'RuleShakeRequest' <ip> <port>
                try:
                    ip = args[1]
                    port = int(args[2])
                    self._receiveRuleVMHelper = ReceiveRuleVMHelper(self, ip, port)
                    self._receiveRuleVMHelper.accept()
                    return
                except Exception as e:
                    msg = 'Unable to process request to send a new rule.\n' + repr(e)
                    message = self._format_new_log_entry(evtype, evtarget, self.target, [msg])
            else:
                if self._amgine is not None:
                    unsafe = self._amgine.apply_dcc_receive_rules(evargs[0])
                    safe_display = evargs[0][:5] + '...' + evargs[0][-5:]
                    text = unsafe + '\n' + safe_display
                    message = self._format_new_log_entry(evtype, evtarget, self.target, [text])
                else:
                    message = self._format_new_log_entry(evtype, evtarget, self.target, evargs)
        elif evtype == 'input':
            message = self._format_new_log_entry(evtype, evtarget, self._nick, evargs)
        else:
            message = self._format_new_log_entry(evtype, evtarget, self.target, ['Unkown message type.'])
        # add the message
        self._receive_view.vm_log(message)
        self._last_source = evsource
        # show page as updated
        if self._appdata['connection_vm'] is not None:
            self._appdata['connection_vm'].page_updated(self.server, self.target)

    def handle_on_connect(self, connection):
        SendReceiveViewModel.handle_on_connect(self, connection)
        message = 'You are now connected to %s.' % (self.target)
        self.handle_client_event(self.target, 'connected', self.server, self.server, [message])

    def handle_on_disconnect(self):
        message = 'You are now disconnected from %s.' % (self.target)
        self.handle_client_event(self.target, 'disconnected', self.server, self.server, [message])
        SendReceiveViewModel.handle_on_disconnect(self)
        self._remove_helpers()

    def handle_on_remove(self):
        SendReceiveViewModel.handle_on_remove(self)
        if self.connected:
            self._connection.disconnect()
        self._remove_helpers()
            
    def _remove_helpers(self):
        if self._receiveRuleVMHelper is not None:
            self._receiveRuleVMHelper.stop()
        if self._sendRuleVMHelper is not None:
            self._sendRuleVMHelper.stop()

    # send view methods

    def _message_split(self, message):
        if len(message) <= self._amgine._dcc_pre_apply_maxlen:
            return [message]
        else:
            words = message.split()
            lines = []
            i = 0
            len_words = len(words)
            while i < len_words:
                len_line = 0
                line = []
                while len_line < self._amgine._dcc_pre_apply_maxlen:
                    wlen = len(words[i])
                    if len_line + wlen <= self._amgine._dcc_pre_apply_maxlen:
                        line.append(words[i])
                        len_line += wlen
                        i += 1
                        if i == len_words:
                            lines.append(line)
                            break
                    else:
                        lines.append(line)
                        break
            return [' '.join(i) for i in lines]

    def dccmsg(self, message=''):
        # message is user input
        unsafe = message.strip()
        if len(unsafe):
            if self._amgine is not None:
                msgs = self._message_split(unsafe)
                for m in msgs:
                    display = m + '\n'
                    safe_list = self._amgine.apply_dcc_send_rules(m)
                    # accumulate the display text
                    for safe in safe_list:
                        safe_display = safe[:5] + '...' + safe[-5:]
                        display += safe_display + ' '
                        # send the safe message
                        self._connection.privmsg(safe)
                    self.handle_client_event(self.target,
                                             'input',
                                             self.server,
                                             self._nick,
                                             [display])
            else:
                raise Exception("Missing rotary encryption rules. Will not send your message.")

    def handle_view_send(self, message):
        """ the user has entered text to send.
        send the message over the network.
        """
        if not self.connected:
            return
        else:
            # just send it
            try:
                self.dccmsg(message)
            except Exception as e:
                message = repr(e)
                self.handle_client_event(self._target,
                                         'exception',
                                         self._server,
                                         'exception',
                                         [message])

    # initial open methods

    def receive_opening_privmsg(self, message):
        if self.connected and len(message) > 0:
            self.handle_client_event(self._target,
                                     'privmsg',
                                     self._target,
                                     [message])

    # ruleshake methods
    
    def th_confirm_send_new_rule(self):
        question = 'Are you sure that you and {0} want to a create new rotary encryption rule?\n' \
                   'If you do then it will also be sent to {0}?\n' \
                   'When the transfer is complete the new rule will replace the current rule for you and {0}.'.format(self.target)
        self.view.question_yes_no('New Rotary Encryption Rule',
                                  question,
                                  self.begin_send_ruleshake)

    def begin_send_ruleshake(self, target):
        # start a dcc server
        # send its address and port in the handshake
        if self._receiveRuleVMHelper is None:
            dcc = self._appdata['client'].dcc_listen(dcctype='raw')
            if self._sendRuleVMHelper is None or not self._sendRuleVMHelper.busy: 
                self._sendRuleVMHelper = SendRuleVMHelper(self, dcc)
                self._sendRuleVMHelper.request()
            else:
                self.view.message_warn('You are already sending a rule.')
        else:
            if not self._receiveRuleVMHelper.busy:
                dcc = self._appdata['client'].dcc_listen(dcctype='raw')
                if self._sendRuleVMHelper is None or not self._sendRuleVMHelper.busy: 
                    self._sendRuleVMHelper = SendRuleVMHelper(self, dcc)
                    self._sendRuleVMHelper.request()
                else:
                    self.view.message_warn('You are already sending a rule.')
            else:
                self.view.message_warn('You are already receiving a rule.')